MVC是現在很流行的程式架構,因為按照功能分開後,更容易擴充和維護,其中的View會寫成接收外來的變數來呈現資料,不處理資料取得和商業邏輯,因此便有了各種的樣板引擎來讓這件事情更容易達成
今天要介紹的是一款PHP的樣板引擎,它沒有名稱,也不有名,來自這一篇文章Template Engine,簡體翻譯,但筆者卻非常推崇,原因無他,就是非常的簡單易用,通常樣板引擎需要另外安裝,有著專用的語法,用來呈現、過濾資料,並且可以用好幾個小樣板,組合出畫面,每一個小樣板都可以重複使用,避免重複開發
而這款卻不需安裝,僅用一個class便實現樣板引擎的功能,甚至還有快取功能,不需要學新的語法,因為直接使用php語言,這有好有壞,設計師需要學習php語言,但雖然如此,用到的php其實不會太複雜,跟學習smarty相比,應該差不多難度,另外一個疑慮是,如果你的網站允許使用者上傳樣板修改版面,使用php語言變成自由度太大了,無法防止被上傳包含惡意程式碼的樣板,不過這篇文章是在2003年寫的,如今都是使用修改css的方式讓使用者修改版面,所以這個疑慮也不存在了
事不宜遲,來看看這個class到底長的怎樣
class Template {
var $vars; /// Holds all the template variables
/**
* Constructor
*
* @param $file string the file name you want to load
*/
function Template($file = null) {
$this->file = $file;
}
/**
* Set a template variable.
*/
function set($name, $value) {
$this->vars[$name] = is_object($value) ? $value->fetch() : $value;
}
/**
* Open, parse, and return the template file.
*
* @param $file string the template file name
*/
function fetch($file = null) {
if(!$file) $file = $this->file;
extract($this->vars); // Extract the vars to local namespace
ob_start(); // Start output buffering
include($file); // Include the file
$contents = ob_get_contents(); // Get the contents of the buffer
ob_end_clean(); // End buffering and discard
return $contents; // Return the contents
}
}
/**
* An extension to Template that provides automatic caching of
* template contents.
*/
class CachedTemplate extends Template {
var $cache_id;
var $expire;
var $cached;
/**
* Constructor.
*
* @param $cache_id string unique cache identifier
* @param $expire int number of seconds the cache will live
*/
function CachedTemplate($cache_id = null, $expire = 900) {
$this->Template();
$this->cache_id = $cache_id ? 'cache/' . md5($cache_id) : $cache_id;
$this->expire = $expire;
}
/**
* Test to see whether the currently loaded cache_id has a valid
* corrosponding cache file.
*/
function is_cached() {
if($this->cached) return true;
// Passed a cache_id?
if(!$this->cache_id) return false;
// Cache file exists?
if(!file_exists($this->cache_id)) return false;
// Can get the time of the file?
if(!($mtime = filemtime($this->cache_id))) return false;
// Cache expired?
if(($mtime + $this->expire) < time()) {
@unlink($this->cache_id);
return false;
}
else {
/**
* Cache the results of this is_cached() call. Why? So
* we don't have to double the overhead for each template.
* If we didn't cache, it would be hitting the file system
* twice as much (file_exists() & filemtime() [twice each]).
*/
$this->cached = true;
return true;
}
}
/**
* This function returns a cached copy of a template (if it exists),
* otherwise, it parses it as normal and caches the content.
*
* @param $file string the template file
*/
function fetch_cache($file) {
if($this->is_cached()) {
$fp = @fopen($this->cache_id, 'r');
$contents = fread($fp, filesize($this->cache_id));
fclose($fp);
return $contents;
}
else {
$contents = $this->fetch($file);
// Write the cache
if($fp = @fopen($this->cache_id, 'w')) {
fwrite($fp, $contents);
fclose($fp);
}
else {
die('Unable to write cache.');
}
return $contents;
}
}
}
可以看到有2個class,第1個實現樣板功能,第2個繼承第1個進一步實現快取功能,原理相當簡單,看fetch方法便知道,利用extract傳進來的陣列變成鍵值為名稱的變數,然後使用緩衝區,將樣板檔引用執行,便會自動套用變數把html產生,將結果存在變數內,然後再輸出緩衝並回傳,就可以取得套好的html字串了,真的非常有巧思,樣板檔會長這樣子
<html>
<head>
<title><?=$title;?></title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1><?=$title;?></h1>
<?=$content;?>
</body>
</html>
<table cellpadding="3" border="0" cellspacing="1" bgcolor="#CCCCCC">
<tr>
<td bgcolor="#F0F0F0">Id</td>
<td bgcolor="#F0F0F0">Name</td>
<td bgcolor="#F0F0F0">Email</td>
<td bgcolor="#F0F0F0">Banned</td>
</tr>
<?php foreach($users as $user): ?>
<tr>
<td bgcolor="#FFFFFF" align="center"><?=$user['id'];?></td>
<td bgcolor="#FFFFFF"><?=$user['name'];?></td>
<td bgcolor="#FFFFFF"><a href="mailto:<?=$user['email'];?>"><?=$user['email'];?></a></td>
<td bgcolor="#FFFFFF" align="center"><?=($user['banned'] ? 'X' : ' ');?></td>
</tr>
<?php endforeach; ?>
</table>
絕大部分是html,少部分是php輸出,之後使用方法如下:
$tpl = new Template('index.tpl'); // this is the outer template
$tpl->set('title', 'User List');
$body = new Template('body.tpl'); // This is the inner template
/*
* The get_list() method of the User class simply runs a query on
* a database - nothing fancy or complex going on here.
*/
$body->set('user_list', $user->get_list());
$tpl->set('content', $body);
echo $tpl->fetch('index.tpl');
只需要讀取樣板檔,然後填入樣板檔內定義的變數,甚至可以填入另一個樣板檔,達到重複使用的效果,是不是非常簡單易用呢,最後說明一下快取的部分,每次存取的時候,判斷是否有相同快取檔存在,快取檔檔名會使用傳入的字串編成md5,作者建議使用網址帶get的參數,這樣參數不同時,快取得檔案也不同,然後可以設置快取過期時間,會自動檢查快取檔到當前時間是否已超過過期時間,若還沒則直接讀取快取檔內容,超過則重新建立,算是方便易用的快取方式喔~